#ifndef __RINGBUFFER_H__
#define __RINGBUFFER_H__

#include <string.h>
#include <mutex>

namespace pi {
namespace utils {


typedef std::mutex Mutex;
typedef std::mutex MutexRW;
typedef std::unique_lock<MutexRW> ReadMutex;
typedef std::unique_lock<MutexRW> WriteMutex;

template <typename BufType=unsigned char>
class RingBuffer
{
public:
    RingBuffer(int size=1024)
        :data(0), dataSize(size), readIdx(0), writeIdx(0)
    {
        data = new BufType[size];
    }

    virtual ~RingBuffer()
    {
        if( data ) delete[] data;
    }

    virtual int clear()
    {
        WriteMutex lock(mutexBuf);

        readIdx = 0;
        writeIdx = 0;

        return 0;
    }

    virtual int total() const
    {
        return dataSize;
    }

    virtual int used()
    {
        ReadMutex lock(mutexBuf);

        int tmp = writeIdx-readIdx;
        if( tmp<0 ) tmp += dataSize;

        return tmp;
    }

    virtual int left()
    {
        ReadMutex lock(mutexBuf);

        int tmp = readIdx-writeIdx;
        if( tmp<=0 ) tmp += dataSize;

        return tmp;
    }

    virtual bool write(BufType* buf, int length=1)
    {
        if( left()<length || length<=0 ) return false;

        {
            WriteMutex lock(mutexBuf);

            int ni = writeIdx + length;
            if( ni > dataSize ) {
                int n2 = ni - dataSize;
                int n1 = length - n2;

                memcpy(data+writeIdx*sizeof(BufType), buf, sizeof(BufType)*n1);
                memcpy(data, buf+n1*sizeof(BufType), sizeof(BufType)*n2);

                writeIdx = ni - dataSize;
            } else {
                memcpy(data+writeIdx*sizeof(BufType), buf, sizeof(BufType)*length);

                writeIdx = ni;
            }
        }

        return true;
    }

    virtual bool read(BufType* buf, int length=1)
    {
        if( used()<length || length<=0 ) return false;

        {
            WriteMutex lock(mutexBuf);

            int ni = readIdx + length;
            if( ni > dataSize ) {
                int n2 = ni - dataSize;
                int n1 = length - n2;

                memcpy(buf, data+readIdx*sizeof(BufType), sizeof(BufType)*n1);
                memcpy(buf+n1*sizeof(BufType), data, sizeof(BufType)*n2);

                readIdx = ni - dataSize;
            } else {
                memcpy(buf, data+readIdx*sizeof(BufType), sizeof(BufType)*length);

                readIdx = ni;
            }
        }

        return true;
    }


protected:
    BufType*    data;
    int         dataSize,
                readIdx, writeIdx;

    MutexRW     mutexBuf;
};

} } // end of namespace pi::utils


#endif // __RINGBUFFER_H__
